﻿namespace NTLib.Core.Threading
{
    using NTLib.Core.Memory;
    using NTLib.Core.Tasks;
    using System;
    using System.Threading.Tasks;
    using System.Threading;
    using System.Runtime.CompilerServices;


    /// <summary>
    /// Provide a ILocker implementation using the AsyncLock Class.
    /// </summary>
    public sealed class AsyncLocker : Disposable, ILocker
    {
        #region Fields

        private readonly AsyncLock _locker;

        #endregion

        #region CTOR

        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncLocker"/> class.
        /// </summary>
        public AsyncLocker()
        {
            this._locker = new AsyncLock();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AsyncLocker"/> class.
        /// </summary>
        /// <param name="locker">The locker.</param>
        public AsyncLocker(AsyncLock locker)
        {
            this._locker = locker;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Lock(bool read = false)
        {
            base.CheckDisposed();
            this._locker.LockAsync().Wait();
        }

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Task LockAsync(bool read = false, CancellationToken token = default(CancellationToken))
        {
            base.CheckDisposed();
            return this._locker.WaitOneAsync(token);
        }

        /// <summary>
        /// Releases the specified access.
        /// </summary>
        /// <param name="fromRead">if set to <c>true</c> the acces will be lock from read depend of the locker item used.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Release(bool fromRead = false)
        {
            base.CheckDisposed();
            this._locker.ReleaseOne();
        }

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public IDisposable ScopeLock(bool read = false)
        {
            base.CheckDisposed();
            return this._locker.LockAsync().WaitResult();
        }

        /// <summary>
        /// Locks the specified access.
        /// </summary>
        /// <param name="read">if set to <c>true</c> the acces will be lock for read depend of the locker item used.</param>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public async Task<IDisposable> ScopeLockAsync(bool read = false, CancellationToken token = default(CancellationToken))
        {
            base.CheckDisposed();
            return await this._locker.LockAsync();
        }

        #endregion
    }
}
